package com.core.filedb;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 文件引擎
 */
@Slf4j
public class DataEngine {
    /**
     * 数据目录
     */
    private static final String path = "/home/etl/v1.0/data/";
    /**
     * 缓存
     */
    private static Map<String, Object> cache = new HashMap();

    /**
     * 一个表，一个线程
     */
    private static ConcurrentHashMap<String, ExecutorService> workManager = new ConcurrentHashMap<>();


    /**
     * 取得工作线程
     *
     * @param tableName
     * @return
     */
    private static ExecutorService getWorker(String tableName) {
        //尽量用同一个线程
        ExecutorService worker = workManager.get(tableName);
        if (worker == null || worker.isTerminated() || worker.isShutdown()) {
            synchronized (DataEngine.class) {
                worker = workManager.get(tableName);
                if (worker == null || worker.isTerminated() || worker.isShutdown()) {
                    worker = Executors.newSingleThreadExecutor();
                    workManager.put(tableName, worker);
                }
            }
        }
        return worker;
    }

    /**
     * 取得记录
     *
     * @param tableName
     * @param id
     */
    private static File getRecord(String tableName, String id) {
        String dataPath = path + tableName + "/" + id;
        File file = new File(dataPath);
        file.setReadable(true);
        file.setWritable(true);
        file.setExecutable(true);
        return file;
    }

    /**
     * 写数据
     *
     * @param tableName
     * @param data
     * @param <T>
     */
    private static <T extends BaseEntity> void writeRecord(String tableName, T data) {
        File file = getRecord(tableName, data.getId());
        if (!file.exists()) {
            file.getParentFile().mkdirs();
        }
        try {
            String txt = JSONUtil.toJsonStr(data);
            Files.write(Paths.get(file.getAbsolutePath()), txt.getBytes());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 查询
     *
     * @param tableName
     * @param clasz
     * @return
     */
    public static <T extends BaseEntity> List<T> data(String tableName, Class<T> clasz) throws Exception {
        if (tableName == null || tableName.length() == 0) {
            throw new RuntimeException("表名不能为空");
        }
        //取得工作线程
        ExecutorService worker = getWorker(tableName);
        Future<List<T>> f = worker.submit(() -> {
            log.info(" Thread-" + Thread.currentThread().getId() + " 查询表" + tableName + "数据");

            List<T> resList = (List<T>) cache.get(tableName);
            if (resList != null) {
                return resList;
            }
            resList = new ArrayList<>();
            String basePath = path + tableName;
            T vo;
            File dir = new File(basePath);
            for (File file : dir.listFiles()) {
                try {
                    String txt = new String(Files.readAllBytes(Paths.get(file.getAbsolutePath())));
                    vo = JSONUtil.toBean(txt, clasz);
                    if (vo != null) {
                        resList.add(vo);
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            cache.put(tableName, resList);
            return resList;
        });
        return f.get();
    }


    /**
     * 新增
     *
     * @param tableName
     * @param data
     * @return
     */
    public static <T extends BaseEntity> int insert(String tableName, T data) throws Exception {
        if (tableName == null || tableName.length() == 0) {
            throw new RuntimeException("表名不能为空");
        }
        //取得工作线程
        ExecutorService worker = getWorker(tableName);
        Future<Integer> f = worker.submit(() -> {
            log.info(" Thread-" + Thread.currentThread().getId() + " 插入表" + tableName + "数据");
            if (data.getId() == null || data.getId().length() == 0) {
                data.setId(String.valueOf(IdUtil.getSnowflake(1, 2).nextId()));
            }
            writeRecord(tableName, (T) data);
            cache.remove(tableName);
            return 1;
        });
        return f.get();
    }


    /**
     * 更新
     *
     * @param tableName
     * @param data
     * @return
     */
    public static <T extends BaseEntity> int update(String tableName, T data) throws Exception {
        if (tableName == null || tableName.length() == 0) {
            throw new RuntimeException("表名不能为空");
        }
        //取得工作线程
        ExecutorService worker = getWorker(tableName);
        Future<Integer> f = worker.submit(() -> {
            log.info(" Thread-" + Thread.currentThread().getId() + " 更新表" + tableName + "数据");
            if (data.getId() == null || data.getId().length() == 0) {
                return 0;
            }
            writeRecord(tableName, (T) data);
            cache.remove(tableName);
            return 1;
        });
        return f.get();

    }


    /**
     * 删除
     *
     * @param tableName
     * @param id
     * @return
     */
    public static <T extends BaseEntity> int delete(String tableName, String id) throws Exception {
        if (tableName == null || tableName.length() == 0) {
            throw new RuntimeException("表名不能为空");
        }
        //取得工作线程
        ExecutorService worker = getWorker(tableName);
        Future<Integer> f = worker.submit(() -> {
            log.info(" Thread-" + Thread.currentThread().getId() + " 删除表" + tableName + "数据");
            if (id == null || id.length() == 0) {
                return 0;
            }
            File file = getRecord(tableName, id);
            if (file.exists()) {
                file.delete();
            }
            cache.remove(tableName);
            return 1;
        });
        return f.get();
    }


}
